/**
 * Copyright (c) 2009 Netflix-Flex_API Contributors.
 * 
 * Permission is hereby granted, free of charge, to any person obtaining a copy of 
 * this software and associated documentation files (the "Software"), to deal in 
 * the Software without restriction, including without limitation the rights to 
 * use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies 
 * of the Software, and to permit persons to whom the Software is furnished to do 
 * so, subject to the following conditions:
 * 
 * The above copyright notice and this permission notice shall be included in all 
 * copies or substantial portions of the Software.
 * 
 * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
 * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
 * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
 * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
 * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
 * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
 * SOFTWARE. 
 * */
package com.netflix.webapis.catalog.model
{
	import com.netflix.webapis.catalog.TitlesService;
	import com.netflix.webapis.catalog.params.CatalogParams;
	import com.netflix.webapis.catalog.vo.LinkItemVO;
	import com.netflix.webapis.events.NetflixFaultEvent;
	import com.netflix.webapis.events.NetflixPropertyChangeEvent;
	import com.netflix.webapis.events.NetflixResultEvent;
	
	import flash.events.Event;
	import flash.events.EventDispatcher;
	import flash.events.IEventDispatcher;
	
	import mx.utils.ObjectUtil;
	
	/**
	* Result Event.
	*/	
	[Event(name="result",type="com.netflix.webapis.events.NetflixResultEvent")]
	/**
	* Fault Event.
	*/	
	[Event(name="fault",type="com.netflix.webapis.events.NetflixFaultEvent")]
	/**
	* Fired when a property is changed.
	*/	
	[Event(name="propertyChanged",type="com.netflix.webapis.events.NetflixPropertyChangeEvent")]
	
	/**
	 * Base Model Class for all Catalog Items. 
	 * @author jonbcampos
	 * 
	 */	
	public class CatalogItemModelBase extends EventDispatcher
	{
		public function CatalogItemModelBase(target:IEventDispatcher=null)
		{
			super(target);
		}
		
		//static properties
		public static const EXPAND_SYNOPSIS:String = "synopsis";
		public static const EXPAND_FORMATS:String = "format_availability";
		public static const EXPAND_SCREEN_FORMATS:String = "screen_formats";
		public static const EXPAND_CAST:String = "cast";
		public static const EXPAND_DIRECTORS:String = "directors";
		public static const EXPAND_LANGUAGES_AND_AUDIO:String = "languages_and_audio";
		public static const EXPAND_SEASONS:String = "seasons";
		public static const EXPAND_EPISODES:String = "episodes";
		public static const EXPAND_DISCS:String = "discs";
		public static const EXPAND_SIMILARS:String = "similars";
		public static const EXPAND_FILMOGRAPHY:String = "filmography";
		public static const EXPAND_BONUS_MATERIALS:String = "bonus_materials";
		public static const EXPAND_AWARDS:String = "awards";
		
		//properties
		/**
		 * Netflix URL Id. Not a specific unique id number. 
		 */		
		public var id:String;
		/**
		 * Type of Catalog item such as movie, season, etc. 
		 */		
		public var titleType:String;
		/**
		 * Short Verison of Catalog Title. 
		 */		
		public var titleShort:String;
		/**
		 * Complete Catalog Item Title. 
		 */		
		public var titleRegular:String;
		/**
		 * Box Art: Small Version. 
		 */		
		public var boxArtSmall:String;
		/**
		 * Box Art: Medium Version. 
		 */		
		public var boxArtMedium:String;
		/**
		 * Box Art: Large Version. 
		 */		
		public var boxArtLarge:String;
		/**
		 * Year of release. 
		 */		
		public var releaseYear:Number;
		/**
		 * Runtime in seconds. 
		 */		
		public var runtime:Number;
		/**
		 * Average Popular Rating. 
		 */		
		public var averageRating:Number;
		
		//additional categories
		/**
		 * Array of Catalogy Items. Can be matched with items from the Categoies Service.
		 * 
		 * @see com.netflix.webapis.catalog.CategoriesService 
		 */		
		public var categories:Array;
		//additional links
		/**
		 * Array of Link Items.
		 */		
		public var links:Array;
		
		//links
		/**
		 * Catalog Item's Synopsis Link Item. 
		 */		
		public var synopsis:LinkItemVO;
		/**
		 * Catalog Item's Cast Link Item. 
		 */		
		public var cast:LinkItemVO;
		/**
		 * Catalog Item's Directors Link Item. 
		 */		
		public var directors:LinkItemVO;
		/**
		 * Catalog Item's Screen Formats Link Item. 
		 */		
		public var screenFormats:LinkItemVO;
		/**
		 * Catalog Item's Languages and Audio Link Item. 
		 */		
		public var languagesAndAudio:LinkItemVO;
		/**
		 * Catalog Item's Seasons Link Item. 
		 */		
		public var seasons:LinkItemVO;
		/**
		 * Catalog Item's Episodes Link Item.
		 */		
		public var episodes:LinkItemVO;
		/**
		 * Catalog Item's Similars Link Item.
		 */		
		public var similars:LinkItemVO;
		/**
		 * Catalog Item's Official Site Link Item.
		 */		
		public var officialSite:LinkItemVO;
		/**
		 * Catalog Item's Web Page Link Item.
		 */		
		public var webPage:LinkItemVO;
		/**
		 * Catalog Item's Formats Link Item.
		 */		
		public var formats:LinkItemVO;
		/**
		 * Catalog Item's Discs Link Item.
		 */		
		public var discs:LinkItemVO;
		
		//expand items
		private var _castList:Array;
		[Bindable("castListChanged")]
		/**
		 * Catalog Item's Cast List Expansion. Array of PersonVO.
		 * 
		 * <p>Dispatches the <code>NetflixPropertyChangeEvent</code> and <i>"castListChanged"</i> event when changed.</p>
		 * 
		 * @return 
		 * 
		 * @see com.netflix.webapis.catalog.vo.PersonVO
		 * @see com.netflix.webapis.events.NetflixPropertyChangeEvent
		 */		
		public function get castList():Array
		{
			return _castList;
		}

		public function set castList(v:Array):void
		{
			if(v!=_castList){
				dispatchEvent(new NetflixPropertyChangeEvent(NetflixPropertyChangeEvent.PROPERTY_CHANGED,v,_castList,"castList",this));
				dispatchEvent(new Event("castListChanged"));
				_castList = v;
			}
		}

		private var _formatsList:Array;
		[Bindable("formatsListChanged")]
		/**
		 * Catalog Item's Formats List Expansion. Array of TitleFormatVO.
		 * 
		 * <p>Dispatches the <code>NetflixPropertyChangeEvent</code> and <i>"formatsListChanged"</i> event when changed.</p>
		 * 
		 * @return 
		 * 
		 * @see com.netflix.webapis.catalog.vo.TitleFormatVO
		 * @see com.netflix.webapis.events.NetflixPropertyChangeEvent
		 */
		public function get formatsList():Array
		{
			return _formatsList;
		}

		public function set formatsList(v:Array):void
		{
			if(v!=_formatsList){
				dispatchEvent(new NetflixPropertyChangeEvent(NetflixPropertyChangeEvent.PROPERTY_CHANGED,v,_formatsList,"formatsList",this));
				dispatchEvent(new Event("formatsListChanged"));
				_formatsList = v;
			}
		}

		private var _screenFormatsList:Array;
		[Bindable("screenFormatsListChanged")]
		/**
		 * Catalog Item's Screen Formats List Expansion. Array of ScreenFormatVO.
		 * 
		 * <p>Dispatches the <code>NetflixPropertyChangeEvent</code> and <i>"screenFormatsListChanged"</i> event when changed.</p>
		 * 
		 * @return 
		 * 
		 * @see com.netflix.webapis.catalog.vo.ScreenFormatVO
		 * @see com.netflix.webapis.events.NetflixPropertyChangeEvent
		 */
		public function get screenFormatsList():Array
		{
			return _screenFormatsList;
		}

		public function set screenFormatsList(v:Array):void
		{
			if(v!=_screenFormatsList){
				dispatchEvent(new NetflixPropertyChangeEvent(NetflixPropertyChangeEvent.PROPERTY_CHANGED,v,_screenFormatsList,"screenFormatsList",this));
				dispatchEvent(new Event("screenFormatsListChanged"));
				_screenFormatsList = v;
			}
		}

		private var _similarsList:Array;
		[Bindable("similarsListChanged")]
		/**
		 * Catalog Item's Similars List Expansion. Array of CatalogItemModelBases.
		 * 
		 * <p>Dispatches the <code>NetflixPropertyChangeEvent</code> and <i>"similarsListChanged"</i> event when changed.</p>
		 * 
		 * @return 
		 * 
		 * @see com.netflix.webapis.events.NetflixPropertyChangeEvent
		 */
		public function get similarsList():Array
		{
			return _similarsList;
		}

		public function set similarsList(v:Array):void
		{
			if(v!=_similarsList){
				dispatchEvent(new NetflixPropertyChangeEvent(NetflixPropertyChangeEvent.PROPERTY_CHANGED,v,_similarsList,"similarsList",this));
				dispatchEvent(new Event("similarsListChanged"));
				_similarsList = v;
			}
		}

		private var _synopsisString:String;
		[Bindable("synopsisStringChanged")]
		/**
		 * Catalog Item's Synopsis Expansion.
		 * 
		 * <p>Dispatches the <code>NetflixPropertyChangeEvent</code> and <i>"synopsisStringChanged"</i> event when changed.</p>
		 * 
		 * @return 
		 * 
		 * @see com.netflix.webapis.events.NetflixPropertyChangeEvent
		 */
		public function get synopsisString():String
		{
			return _synopsisString;
		}

		public function set synopsisString(v:String):void
		{
			if(v!=_synopsisString){
				dispatchEvent(new NetflixPropertyChangeEvent(NetflixPropertyChangeEvent.PROPERTY_CHANGED,v,_synopsisString,"synopsisString",this));
				dispatchEvent(new Event("synopsisStringChanged"));
				_synopsisString = v;
			}
		}

		private var _directorList:Array;
		[Bindable("directorListChanged")]
		/**
		 * Catalog Item's Director List Expansion. Array of PersonVO.
		 * 
		 * <p>Dispatches the <code>NetflixPropertyChangeEvent</code> and <i>"directorListChanged"</i> event when changed.</p>
		 * 
		 * @return 
		 * 
		 * @see com.netflix.webapis.catalog.vo.PersonVO
		 * @see com.netflix.webapis.events.NetflixPropertyChangeEvent
		 */
		public function get directorList():Array
		{
			return _directorList;
		}

		public function set directorList(v:Array):void
		{
			if(v!=_directorList){
				dispatchEvent(new NetflixPropertyChangeEvent(NetflixPropertyChangeEvent.PROPERTY_CHANGED,v,_directorList,"directorList",this));
				dispatchEvent(new Event("directorListChanged"));
				_directorList = v;
			}
		}

		private var _awardsList:Array;
		[Bindable("awardsListChanged")]
		/**
		 * Catalog Item's Award List Expansion. Array of AwardNomineeVO.
		 * 
		 * <p>Dispatches the <code>NetflixPropertyChangeEvent</code> and <i>"awardsListChanged"</i> event when changed.</p>
		 * 
		 * @return 
		 * 
		 * @see com.netflix.webapis.catalog.vo.AwardNomineeVO
		 * @see com.netflix.webapis.events.NetflixPropertyChangeEvent
		 */
		public function get awardsList():Array
		{
			return _awardsList;
		}

		public function set awardsList(v:Array):void
		{
			if(v!=_awardsList){
				dispatchEvent(new NetflixPropertyChangeEvent(NetflixPropertyChangeEvent.PROPERTY_CHANGED,v,_awardsList,"awardsList",this));
				dispatchEvent(new Event("awardsListChanged"));
				_awardsList = v;
			}
		}

		private var _languagesAndAudioList:Array;
		[Bindable("languageAndAudioListChanged")]
		/**
		 * Catalog Item's Language and Audio List Expansion. Array of LangaugeFormatVO.
		 * 
		 * <p>Dispatches the <code>NetflixPropertyChangeEvent</code> and <i>"languageAndAudioListChanged"</i> event when changed.</p>
		 * 
		 * @return 
		 * 
		 * @see com.netflix.webapis.catalog.vo.LangaugeFormatVO
		 * @see com.netflix.webapis.events.NetflixPropertyChangeEvent
		 */
		public function get languagesAndAudioList():Array
		{
			return _languagesAndAudioList;
		}

		public function set languagesAndAudioList(v:Array):void
		{
			if(v!=_languagesAndAudioList){
				dispatchEvent(new NetflixPropertyChangeEvent(NetflixPropertyChangeEvent.PROPERTY_CHANGED,v,_languagesAndAudioList,"languagesAndAudioList",this));
				dispatchEvent(new Event("languageAndAudioListChanged"));
				_languagesAndAudioList = v;
			}
		}

		
		//---------------------
		//
		// methods
		//
		//---------------------
		/**
		 * Calls to expand the model to include additional information. Calls additional Service calls.
		 * @param expandItem
		 * @param params
		 * 
		 */		
		public function expandProperty(expandItem:String,params:CatalogParams=null):void
		{
			if(!params)
				params = new CatalogParams();
			var service:TitlesService = new TitlesService();
			params.expand = expandItem;
			params.retrieveExpansionOnly = true;
			service.addEventListener(NetflixResultEvent.RESULT,onExpandResult);
			service.addEventListener(NetflixFaultEvent.FAULT,onExpandFault);
			service.send(params);
			
			function onExpandResult(event:NetflixResultEvent):void
			{
				//set expansion list
				switch(expandItem){
					case EXPAND_SYNOPSIS:
						var s:Array = event.result as Array;
						synopsisString = s[0].toString();
					break;
					case EXPAND_FORMATS:
						formatsList = event.result as Array;
					break;
					case EXPAND_SCREEN_FORMATS:
						screenFormatsList = event.result as Array;
					break;
					case EXPAND_CAST:
						castList = event.result as Array;
					break;
					case EXPAND_DIRECTORS:
						directorList = event.result as Array;
					break;
					case EXPAND_LANGUAGES_AND_AUDIO:
						languagesAndAudioList = event.result as Array;
					break;
					case EXPAND_SEASONS:
						//TODO add seasons expansion
					break;
					case EXPAND_EPISODES:
						//TODO add episodes expansion
					break;
					case EXPAND_DISCS:
						//TODO add discs expansion
					break;
					case EXPAND_SIMILARS:
						similarsList = event.result as Array;
					break;
					case EXPAND_FILMOGRAPHY:
						//TODO add filmography expansion
					break;
					case EXPAND_BONUS_MATERIALS:
						//TODO add bonus materials expansion
					break;
					case EXPAND_AWARDS:
						awardsList = event.result as Array;
					break;
				}
				dispatchEvent(event);
			}
			
			function onExpandFault(event:NetflixFaultEvent):void
			{
				dispatchEvent(event);
			}
		}
		/**
		 * Calls to retrieve the expanded synopsis. 
		 */		
		public function expandSynopsis():void
		{
			expandProperty(EXPAND_SYNOPSIS);
		}
		/**
		 *  Calls to retrieve the expanded formats. 
		 */		
		public function expandFormats():void
		{
			expandProperty(EXPAND_FORMATS);
		}
		/**
		 *  Calls to retrieve the expanded screen formats. 
		 */		
		public function expandScreenFormats():void
		{
			expandProperty(EXPAND_SCREEN_FORMATS);
		}
		/**
		 *  Calls to retrieve the expanded cast. 
		 */		
		public function expandCast():void
		{
			expandProperty(EXPAND_CAST);
		}
		/**
		 *  Calls to retrieve the expanded directors. 
		 */		
		public function expandDirectors():void
		{
			expandProperty(EXPAND_DIRECTORS);
		}
		/**
		 *  Calls to retrieve the expanded languages and audio. 
		 */		
		public function expandLanguagesAndAudio():void
		{
			expandProperty(EXPAND_LANGUAGES_AND_AUDIO);
		}
		/**
		 *  Calls to retrieve the expanded seasons. 
		 */		
		public function expandSeasons():void
		{
			expandProperty(EXPAND_SEASONS);
		}
		/**
		 *  Calls to retrieve the expanded episodes. 
		 */		
		public function expandEpisodes():void
		{
			expandProperty(EXPAND_EPISODES);
		}
		/**
		 *  Calls to retrieve the expanded discs. 
		 */		
		public function expandDiscs():void
		{
			expandProperty(EXPAND_DISCS);
		}
		/**
		 * Calls to retrieve additional titles that are similar to this CatalogItemModelBase.
		 * @param startIndex
		 * @param maxResults
		 * 
		 */		
		public function expandSimilars(startIndex:int=-1,maxResults:int=-1):void
		{
			var params:CatalogParams = new CatalogParams();
			if(startIndex!=-1)
				params.startIndex = startIndex;
			if(maxResults!=-1)
				params.maxResults = maxResults;
			expandProperty(EXPAND_SIMILARS,params);
		}
		/**
		 *  Calls to retrieve the expanded filmography. 
		 */		
		public function expandFilmography():void
		{
			expandProperty(EXPAND_FILMOGRAPHY);
		}
		/**
		 *  Calls to retrieve the expanded bonus materials. 
		 */		
		public function expandBonusMaterials():void
		{
			expandProperty(EXPAND_BONUS_MATERIALS);
		}
		/**
		 *  Calls to retrieve the expanded awards. 
		 */		
		public function expandAwards():void
		{
			expandProperty(EXPAND_AWARDS);
		}
		
		/**
		 *  Calls to retrieve all the expanded items. 
		 */		
		public function expandAll():void
		{
			expandSynopsis();
			expandFormats();
			expandScreenFormats();
			expandCast();
			expandDirectors();
			expandLanguagesAndAudio();
			expandSeasons();
			expandEpisodes();
			expandDiscs();
			expandSimilars();
			expandFilmography();
			expandBonusMaterials();
			expandAwards();
		}
		
		override public function toString():String
		{
			return ObjectUtil.toString(this);
		}
	}
}